/**
 * Created by Richard Radlherr.
 * Date: 09.07.2006 18:42
 */

using System;
using System.Xml;
using System.Collections.Generic;
using CsGL.OpenGL;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace TagTool.GraphicTypes
{
	
	/**
	 * A Tag is a simple drawing of lines with its own transfromations
	 * The Tag controls its own glList.
	 */
	[Serializable()]
	public class Tag : Shape, IDisposable
	{
		private List< List <ColoredPoint> > lines = new List< List <ColoredPoint> >();
		private List <ColoredPoint> line = null;
		
		private List<uint> glLines = new List< uint>();
		private uint glLine = 0;
		private bool first = true;

		public Tag()
		{
			alpha = 1.0;
		}
		
		public override void Dispose()
		{
			if (line != null)
			{
				line.Clear();
			}
			base.Dispose();
			first = true;
		}
		
		public override double Alpha
		{
			set 
			{
				if (value != alpha)
				{
					alpha = value; 
					
					uint gl;
					int i = 0;
					
					foreach (List <ColoredPoint> line in lines)
					{
						gl = glLines[i];
						glNewList(gl, GL_COMPILE);
						DrawLine(line);
						glEndList();
						
						i++;
					}
				}
			}
		}
		
		public List<List<ColoredPoint>> Lines
		{
			get { return lines; }
		}

		public override void SetPivot()
		{
			double mx = (x2 - x1) / 2;
			double my = (y2 - y1) / 2;
			uint gl;
			int i = 0;
			
			foreach (List <ColoredPoint> line in lines)
			{
				gl = glLines[i];
				
				foreach (ColoredPoint point in line)
				{
					point.Add(-(x1 + mx), -(y1 + my), 0.0);
				}
				
				glNewList(gl, GL_COMPILE);
				DrawLine(line);
				glEndList();
				
				i++;
			}
			
			this.Translate += new PointD(x1 + mx, y1 + my, 0.0);
			
			x1 = -mx;
			y1 = -my;
			x2 = mx;
			y2 = my;
			
		}

		public void AddPoint(ColoredPoint point)
		{
			if (first)
			{
				x1 = x2 = point.X;
				y1 = y2 = point.Y;
				first = false;
			}
			
			if (line == null)
			{
				AddLine();
			}
			line.Add(point);
			if (x1 > point.X)
			{
				x1 = point.X;
			}
			if (x2 < point.X)
			{
				x2 = point.X;
			}
			
			if (y1 > point.Y)
			{
				y1 = point.Y;
			}
			if (y2 < point.Y)
			{
				y2 = point.Y;
			}
				
			
			if (line != null && lines.Count > 0)
			{
				glNewList(glLines[lines.Count - 1], GL_COMPILE);
				DrawLine(lines[lines.Count - 1]);
				glEndList();
			}
			Update();
		}
		
		public override void Save(StreamWriter writer)
		{
			
			try {
				if (lines.Count > 0)
				{
					long ac;
					long st;
			
					// Header
					writer.WriteLine("Tag");
					
					// Length
					st = writer.BaseStream.Position;
					writer.WriteLine(String.Format("{0:000000000000}", 0));
					
					BinaryFormatter b = new BinaryFormatter();
					b.Serialize(writer.BaseStream, lines);
					
					// Length
					ac = writer.BaseStream.Position;
					writer.BaseStream.Seek(st, 0);
					
					writer.WriteLine(String.Format("{0:000000000000}", ac - st));
					writer.BaseStream.Seek(ac, 0);
					
					writer.WriteLine("Transform");
					
					
					// Length
					st = writer.BaseStream.Position;
					writer.WriteLine(String.Format("{0:000000000000}", 0));
					b.Serialize(writer.BaseStream, this.Alpha);
					b.Serialize(writer.BaseStream, this.Angle);
					b.Serialize(writer.BaseStream, this.Direction);
					b.Serialize(writer.BaseStream, this.Rotate);
					b.Serialize(writer.BaseStream, this.Scale);
					b.Serialize(writer.BaseStream, this.Speed);
					b.Serialize(writer.BaseStream, this.Spin);
					b.Serialize(writer.BaseStream, this.Translate);
					b.Serialize(writer.BaseStream, this.Zoom);
					b.Serialize(writer.BaseStream, this.FadeStep);
					
					ac = writer.BaseStream.Position;
					writer.BaseStream.Seek(st, 0);
					
					writer.WriteLine(String.Format("{0:000000000000}", ac - st));
					writer.BaseStream.Seek(ac, 0);
				}
				
			} catch (Exception)
			{
				// eat this fucking exception (hrhr)
			}
		}
		
		public override void Load(StreamReader reader)
		{
			try {
				Dispose();
				BinaryFormatter b = new BinaryFormatter();
				List<List<ColoredPoint>>ls = (List<List<ColoredPoint>>)b.Deserialize(reader.BaseStream);
				foreach(List<ColoredPoint> line in ls)
				{
					AddLine();
					foreach(ColoredPoint p in line)
					{
						AddPoint(p);
					}
				}
				//Console.WriteLine("gl lists created");
				if (ReadHeader(reader, false, "Transform"))
				{
					Alpha = (double)b.Deserialize(reader.BaseStream);
					Angle = (double)b.Deserialize(reader.BaseStream);
					Direction = (PointD)b.Deserialize(reader.BaseStream);
					Rotate = (PointD)b.Deserialize(reader.BaseStream);
					Scale = (PointD)b.Deserialize(reader.BaseStream);
					Speed = (double)b.Deserialize(reader.BaseStream);
					Spin = (double)b.Deserialize(reader.BaseStream);
					Translate =  (PointD)b.Deserialize(reader.BaseStream);
					Zoom =  (PointD)b.Deserialize(reader.BaseStream);
					FadeStep = (double)b.Deserialize(reader.BaseStream);
				}
				
				SetPivot();
				Update();
			} catch (Exception ex)
			{
				Console.WriteLine("Error on loading tag" + ex.StackTrace);
			}
		}
		
		public void AddLine()
		{
			
			if (line != null)
			{
				glNewList(glLine, GL_COMPILE);
				if (lines.Count > 0)
				{
					DrawLine(lines[lines.Count - 1]);
				}
				glEndList();
			}
			
			Update();
			
			line = new List<ColoredPoint>(2048);
			lines.Add(line);
			
			glLine = glGenLists(1);
			glNewList(glLine, GL_COMPILE);
			glEndList();
			
			glLines.Add(glLine);
		}
		
		public void Clear()
		{
			foreach (uint i in glLines )
			{
				glDeleteLists(i, 1);
			}
			glLines.Clear();
			lines.Clear();
			if (line != null)
			{
				line.Clear();
				line = null;
			}
			
			first = true;
			Update();
		}
		
		private void DrawLine(List<ColoredPoint> line)
		{
			CsGL.OpenGL.GL.glBegin(CsGL.OpenGL.GL.GL_TRIANGLE_STRIP);
			
			if (line.Count > 1)
			{
				List<ColoredPoint>.Enumerator curPoint = line.GetEnumerator();
				PointD a = new PointD();
				PointD b = new PointD();
				
				bool first = false;
				PointD start = line[0];
				PointD dir;
				bool toggle = false;
				double d, l;
				
				while(curPoint.MoveNext())
				{
					if (first)
					{
						first = false;
						a = b = curPoint.Current;
					}
					else
					{
						dir = curPoint.Current - start;
						d = dir.X;
						dir.X = -dir.Y;
						dir.Y = d;
						dir = dir.Normalize();
						l = dir.Length;
						if (Math.Abs(l) > 0.0001)
						{
							a = curPoint.Current + dir * curPoint.Current.Width;
							b = curPoint.Current - dir * curPoint.Current.Width;
							start = curPoint.Current;
						}
						else
						{
							continue;
						}
					}
					
					CsGL.OpenGL.GL.glColor4d( curPoint.Current.Color.R, curPoint.Current.Color.G, curPoint.Current.Color.B, curPoint.Current.Color.A * alpha);
					CsGL.OpenGL.GL.glVertex3d(a.X, a.Y, a.Z);
					CsGL.OpenGL.GL.glVertex3d(b.X, b.Y, b.Z);
					
					toggle = !toggle;
				}
				
			}
			
			CsGL.OpenGL.GL.glEnd();
		}

		protected override void Compile()
		{
			foreach(uint line in glLines)
			{
				glCallList(line);
				glFlush();
			}

		}
		
		
	}
	
}
